﻿using System.Configuration;
using System.Data;
using System.Windows;
using System;
using System.IO;
using System.Threading.Tasks;
using XmWinKit.Common;
using Application = System.Windows.Application;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Windows.Interop;

namespace XmWinKit
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private XmWinKit.Views.ChinaCalendarWindow _calendarWindow;
        private IntPtr _hookHandle;
        private const int WH_MOUSE_LL = 14;
        private const int WM_LBUTTONDBLCLK = 0x0203;
        private const int WM_LBUTTONDOWN = 0x0201;
        private const int WM_LBUTTONUP = 0x0202;
        
        // 单例模式所需的互斥体
        private static Mutex _mutex = null;
        private const string MutexName = "XmWinKitCalendarMutex";

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int X;
            public int Y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
        private LowLevelMouseProc _mouseProc;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern IntPtr WindowFromPoint(POINT point);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            // 检查应用程序是否已经运行
            bool createdNew;
            _mutex = new Mutex(true, MutexName, out createdNew);

            if (!createdNew)
            {
                // 应用程序已经运行，通知现有实例并退出
                MessageBox.Show("中国农历日历已经在运行中。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                
                // 可以尝试激活现有实例
                TryActivateExistingInstance();
                
                // 退出当前实例
                Shutdown();
                return;
            }
            
            // 设置全局异常处理
            SetupGlobalExceptionHandling();
            
            base.OnStartup(e);
            
            // 创建托盘图标
         

            // 初始化窗口但不显示
            _calendarWindow = new XmWinKit.Views.ChinaCalendarWindow();
            _calendarWindow.ShowInTaskbar = false;
            _calendarWindow.WindowStyle = WindowStyle.None;
            _calendarWindow.AllowsTransparency = true;
            _calendarWindow.Topmost = true;
            _calendarWindow.ResizeMode = ResizeMode.NoResize;
            _calendarWindow.IsDragMove = false;
            _calendarWindow.Deactivated += CalendarWindow_Deactivated;
            _calendarWindow.Loaded += (s, e) => {
                var workingArea = SystemParameters.WorkArea;
                _calendarWindow.Left = workingArea.Right - _calendarWindow.Width + 10;
                _calendarWindow.Top = workingArea.Bottom - _calendarWindow.Height + 10;
            };

            // 设置鼠标钩子
            _mouseProc = MouseHookProc;
            _hookHandle = SetWindowsHookEx(WH_MOUSE_LL, _mouseProc, IntPtr.Zero, 0);
        }

        private void SetupGlobalExceptionHandling()
        {
            // 处理UI线程未捕获的异常
            this.DispatcherUnhandledException += App_DispatcherUnhandledException;
            
            // 处理非UI线程未捕获的异常
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            
            // 处理Task中未观察到的异常
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
        }

        private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            try
            {
                // 记录错误日志
                ErrorLogger.LogError(e.Exception, "UI线程未处理异常");
                
                // 显示错误弹窗
                ShowErrorDialog(e.Exception, "应用程序遇到错误");
                
                // 标记异常已处理，防止应用程序崩溃
                e.Handled = true;
            }
            catch (Exception ex)
            {
                // 如果错误处理本身出错，记录到系统事件日志
                System.Diagnostics.EventLog.WriteEntry("XmWinKit", 
                    $"错误处理失败: {ex.Message}", 
                    System.Diagnostics.EventLogEntryType.Error);
            }
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            try
            {
                Exception exception = e.ExceptionObject as Exception;
                if (exception != null)
                {
                    // 记录错误日志
                    ErrorLogger.LogError(exception, "非UI线程未处理异常");
                    
                    // 显示错误弹窗
                    ShowErrorDialog(exception, "应用程序遇到严重错误");
                }
            }
            catch (Exception ex)
            {
                // 如果错误处理本身出错，记录到系统事件日志
                System.Diagnostics.EventLog.WriteEntry("XmWinKit", 
                    $"严重错误处理失败: {ex.Message}", 
                    System.Diagnostics.EventLogEntryType.Error);
            }
        }

        private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            try
            {
                // 记录错误日志
                ErrorLogger.LogError(e.Exception, "Task未观察异常");
                
                // 标记异常已观察，防止应用程序终止
                e.SetObserved();
            }
            catch (Exception ex)
            {
                // 如果错误处理本身出错，记录到系统事件日志
                System.Diagnostics.EventLog.WriteEntry("XmWinKit", 
                    $"Task异常处理失败: {ex.Message}", 
                    System.Diagnostics.EventLogEntryType.Error);
            }
        }

        private void ShowErrorDialog(Exception exception, string title)
        {
            try
            {
                string message = $"错误信息：{exception.Message}\n\n" +
                               $"错误类型：{exception.GetType().Name}\n\n" +
                               "详细错误信息已保存到日志文件中。\n\n" +
                               "是否继续运行程序？";

                MessageBoxResult result = MessageBox.Show(
                    message,
                    title,
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Error);

                if (result == MessageBoxResult.No)
                {
                    // 用户选择退出程序
                    this.Shutdown();
                }
            }
            catch
            {
                // 如果弹窗显示失败，至少尝试显示简单消息
                MessageBox.Show("应用程序遇到错误，请查看日志文件。", "错误", 
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                if (wParam == (IntPtr)WM_LBUTTONDOWN)
                {
                    // 解析鼠标事件结构
                    var hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                    var hwnd = WindowFromPoint(hookStruct.pt);
                    
                    // 检查是否点击了系统时钟区域
                    if (hwnd != IntPtr.Zero)
                    {
                        var className = new System.Text.StringBuilder(256);
                        GetClassName(hwnd, className, className.Capacity);
                        
                        // 检查是否是系统托盘时钟窗口
                        if (IsSystemTrayClock(className.ToString(), hookStruct.pt))
                        {
                            // 如果日历窗口已显示则隐藏，否则显示
                            if (_calendarWindow.IsVisible)
                            {
                                _calendarWindow.Hide();
                            }
                            else
                            {
                                ShowCalendarWindowAtBottomRight();
                            }
                            return (IntPtr)1; // 阻止消息继续传递
                        }
                        
                        // 检查点击是否在日历窗口外
                        if (_calendarWindow.IsVisible)
                        {
                            var calendarHwnd = new System.Windows.Interop.WindowInteropHelper(_calendarWindow).Handle;
                            
                            // 获取点击位置的窗口类名
                            var clickedClassName = new System.Text.StringBuilder(256);
                            GetClassName(hwnd, clickedClassName, clickedClassName.Capacity);
                            
                            // 获取点击位置的窗口句柄
                            var clickedHwnd = WindowFromPoint(hookStruct.pt);
                            
                            // 获取日历窗口的Popup
                            var popup = _calendarWindow.FindName("MonthPopup") as System.Windows.Controls.Primitives.Popup;
                            
                            // 如果Popup存在且可见
                            if (popup != null && popup.IsOpen)
                            {
                                // 获取Popup的宿主窗口句柄
                                var hwndSource = HwndSource.FromVisual(popup.Child) as HwndSource;
                                if (hwndSource != null)
                                {
                                    var popupHwnd = hwndSource.Handle;
                                    
                                    // 如果点击的是Popup或其子窗口，不隐藏主窗口
                                    if (clickedHwnd == popupHwnd || IsChild(popupHwnd, clickedHwnd))
                                    {
                                        return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
                                    }
                                }
                            }
                            
                            // 如果点击的不是日历窗口本身，则隐藏窗口
                            if (hwnd != calendarHwnd)
                            {
                                _calendarWindow.Hide();
                            }
                        }
                    }
                }
            }
            return CallNextHookEx(_hookHandle, nCode, wParam, lParam);
        }

        private bool IsSystemTrayClock(string className, POINT point)
        {
            // Windows 10/11 系统托盘时钟的类名是 "TrayClockWClass"
            if (className == "TrayClockWClass")
            {
                // 这个是时钟窗口，直接返回 true
                return true;
            }
            
            // Windows 7 和某些旧版本 Windows 的托盘区可能是 "TrayNotifyWnd"
            if (className == "TrayNotifyWnd")
            {
                // 获取窗口位置
                RECT rect;
                var hwnd = WindowFromPoint(point);
                if (GetWindowRect(hwnd, out rect))
                {
                    // 对于 TrayNotifyWnd，我们只检查右侧部分（大约右 1/4 的区域作为时钟区域）
                    int clockAreaWidth = (rect.Right - rect.Left) / 4;
                    return point.X >= (rect.Right - clockAreaWidth) && point.X <= rect.Right &&
                           point.Y >= rect.Top && point.Y <= rect.Bottom;
                }
            }
            
            return false;
        }

        private void ShowCalendarWindowAtBottomRight()
        {
            var workingArea = SystemParameters.WorkArea;
            _calendarWindow.Left = workingArea.Right - _calendarWindow.Width +10;  // 更靠近右边缘
            _calendarWindow.Top = workingArea.Bottom - _calendarWindow.Height + 10;  // 更靠近底部
            _calendarWindow.Show();
            _calendarWindow.Activate();
        }

        private void CalendarWindow_Deactivated(object sender, EventArgs e)
        {
            _calendarWindow.Hide();
        }

        private void TryActivateExistingInstance()
        {
            try
            {
                // 尝试查找并激活现有实例的主窗口
                Process current = Process.GetCurrentProcess();
                foreach (Process process in Process.GetProcessesByName(current.ProcessName))
                {
                    if (process.Id != current.Id)
                    {
                        // 找到其他实例，尝试激活其窗口
                        NativeMethods.SetForegroundWindow(process.MainWindowHandle);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                // 激活现有实例失败，记录错误但不影响当前实例退出
                ErrorLogger.LogError(ex, "激活现有实例失败");
            }
        }

        protected override void OnExit(ExitEventArgs e)
        {
            if (_hookHandle != IntPtr.Zero)
            {
                UnhookWindowsHookEx(_hookHandle);
                _hookHandle = IntPtr.Zero;
            }
            
            // 释放互斥体
            if (_mutex != null)
            {
                _mutex.ReleaseMutex();
                _mutex.Dispose();
                _mutex = null;
            }
            
            base.OnExit(e);
        }
        
        // 用于激活窗口的本地方法
        private static class NativeMethods
        {
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetForegroundWindow(IntPtr hWnd);
        }

        [DllImport("user32.dll")]
        private static extern bool IsChild(IntPtr hWndParent, IntPtr hWnd);
    }
}
